﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
//using System.Windows.Controls;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using GalaSoft.MvvmLight;
using DeploymentTool.Model;
using GalaSoft.MvvmLight.Command;
using System.Linq;
using Ionic.Zip;
using Ionic.Zlib;
using Newtonsoft.Json;
using SyntaxHighlight;
using MessageBox = System.Windows.MessageBox;
using RichTextBox = Xceed.Wpf.Toolkit.RichTextBox;

namespace DeploymentTool.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        private readonly IDataService _dataService;

        /// <summary>
        /// The <see cref="WelcomeTitle" /> property's name.
        /// </summary>
        public const string WelcomeTitlePropertyName = "WelcomeTitle";

        private string _welcomeTitle = string.Empty;

        /// <summary>
        /// Gets the WelcomeTitle property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string WelcomeTitle
        {
            get
            {
                return _welcomeTitle;
            }

            set
            {
                if (_welcomeTitle == value)
                {
                    return;
                }

                _welcomeTitle = value;
                RaisePropertyChanged(WelcomeTitlePropertyName);
            }
        }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel(IDataService dataService)
        {
            LoadConfig.Execute(null);
            _dataService = dataService;
            _dataService.GetData(
                (item, error) =>
                {
                    if (error != null)
                    {
                        // Report error here
                        return;
                    }

                    WelcomeTitle = item.Title;
                });
            TabIndex = 0;
        }

        public const string TabIndexPropertyName = "TabIndex";
        private int _tabIndex = 1;
        public int TabIndex
        {
            get
            {
                return _tabIndex;
            }
            set
            {
                if (_tabIndex == value)
                {
                    return;
                }

                RaisePropertyChanging(TabIndexPropertyName);
                _tabIndex = value;
                RaisePropertyChanged(TabIndexPropertyName);
            }
        }

        public const string HtmlPropertyName = "Html";
        private string _html = "";
        public string Html
        {
            get
            {
                return _html;
            }

            set
            {
                if (_html == value)
                {
                    return;
                }

                RaisePropertyChanging(HtmlPropertyName);
                _html = value;
                RaisePropertyChanged(HtmlPropertyName);
            }
        }

        public const string ConfigPropertyName = "Config";
        private DepConfig _config = null;
        public DepConfig Config
        {
            get
            {
                return _config;
            }

            set
            {
                if (_config == value)
                {
                    return;
                }

                RaisePropertyChanging(ConfigPropertyName);
                _config = value;
                RaisePropertyChanged(ConfigPropertyName);
            }
        }

        public const string SearchResultPropertyName = "SearchResult";
        private List<DepItem> _searchResult = new List<DepItem>();
        public List<DepItem> SearchResult
        {
            get
            {
                return _searchResult;
            }

            set
            {
                if (_searchResult == value)
                {
                    return;
                }

                RaisePropertyChanging(SearchResultPropertyName);
                _searchResult = value;
                RaisePropertyChanged(SearchResultPropertyName);
                ReplaceCommand.RaiseCanExecuteChanged();
            }
        }

        public const string ReplaceResultPropertyName = "ReplaceResult";
        private List<DepItemReplace> _replaceResult = new List<DepItemReplace>();
        public List<DepItemReplace> ReplaceResult
        {
            get
            {
                return _replaceResult;
            }

            set
            {
                if (_replaceResult == value)
                {
                    return;
                }

                RaisePropertyChanging(ReplaceResultPropertyName);
                _replaceResult = value;
                RaisePropertyChanged(ReplaceResultPropertyName);
                CopyCommand.RaiseCanExecuteChanged();
            }
        }

        /*private RelayCommand<RichTextBox> _searchCommand;
        public RelayCommand<RichTextBox> SearchCommand
        {
            get
            {
                return _searchCommand
                       ?? (_searchCommand = new RelayCommand<RichTextBox>(
                                                rtb =>
                                                    {
                                                        SearchResult.Clear();
                                                        string p = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd).Text;
                                                        if (Config.SearchPatterns.Count <= 0)
                                                        {
                                                            MessageBox.Show("Please define the search pattern first.");
                                                            return;
                                                        }
                                                        var paragraphs = rtb.Document.Blocks.Where(n => n is Paragraph);
                                                        DepItemComparer comparer = new DepItemComparer();
                                                        List<DepItem> list = new List<DepItem>();
                                                        DepItem depItem;
                                                        string file;
                                                        string pText;
                                                        Match match;
                                                        Stopwatch stopwatch = new Stopwatch();
                                                        stopwatch.Start();
                                                        bool isFindMatch = false;
                                                        List<Paragraph> findedParagraphs = new List<Paragraph>();
                                                        try
                                                        {
                                                            foreach (Paragraph paragraph in paragraphs)
                                                            {
                                                                isFindMatch = false;
                                                                pText = new TextRange(paragraph.ContentStart,paragraph.ContentEnd).Text;
                                                                foreach (var search in Config.SearchPatterns.Where(n => n.Enable))
                                                                {
                                                                    match = Regex.Match(pText, search.SearchPattern);
                                                                    if (match.Success)
                                                                    {
                                                                        file = match.ToString().Trim();
                                                                        if (Config.IgnoredExtensions.Contains(new FileInfo(file).Extension.ToLower()))
                                                                            continue;
                                                                        depItem = new DepItem()
                                                                        {
                                                                            IsSelected = true,
                                                                            Value = file
                                                                        };
                                                                        if (!list.Contains(depItem, comparer))
                                                                        {
                                                                            list.Add(depItem);
                                                                            //Highlight(paragraphs, file);
                                                                        }
                                                                        isFindMatch = true;
                                                                        break;
                                                                    }
                                                                    //var results = Regex.Matches(p, Regex.Escape(search.SearchPattern));
                                                                }
                                                                if (isFindMatch)
                                                                {
                                                                    findedParagraphs.Add(paragraph);
                                                                    //paragraph.FontWeight = FontWeights.Bold;
                                                                    //paragraph.Foreground = new SolidColorBrush(Colors.Blue);
                                                                }
                                                            }

                                                            /*foreach (var search in Config.SearchPatterns.Where(n => n.Enable))
                                                            {
                                                                var results = Regex.Matches(p, search.SearchPattern);
                                                                foreach (var item in results)
                                                                {
                                                                    file = item.ToString().Trim();
                                                                    if (Config.IgnoredExtensions.Contains(new FileInfo(file).Extension.ToLower()))
                                                                        continue;
                                                                    depItem = new DepItem()
                                                                    {
                                                                        IsSelected = true,
                                                                        //Value = Regex.Unescape(item.ToString())
                                                                        Value = file
                                                                    };
                                                                    if (!list.Contains(depItem, comparer))
                                                                    {
                                                                        list.Add(depItem);
                                                                        //Highlight(paragraphs, file);
                                                                    }
                                                                }
                                                            }#1#
                                                        }
                                                        catch (Exception er)
                                                        {
                                                            MessageBox.Show(er.ToString(), "Error while executing search command.");
                                                            return;
                                                        }
                                                        stopwatch.Stop();
                                                        SearchResult = list;
                                                        //RaisePropertyChanged(SearchResultPropertyName);
                                                        ReplaceResult = new List<DepItemReplace>();
                                                        MessageBox.Show(list.Count + " - " + stopwatch.ElapsedMilliseconds);
                                                    }));
            }
        }*/

        private RelayCommand<SyntaxHighlightTextBox> _searchCommand;
        public RelayCommand<SyntaxHighlightTextBox> SearchCommand
        {
            get
            {
                return _searchCommand
                       ?? (_searchCommand = new RelayCommand<SyntaxHighlightTextBox>(Search));
            }
        }

        private void Search(SyntaxHighlightTextBox rtb)
        {
            TabControl tab = rtb.Parent as TabControl;
            //(tab.Items[0] as TabItem).Cont
            SearchResult.Clear();
            string p = rtb.Text;
            if (Config.SearchPatterns.Count <= 0)
            {
                MessageBox.Show("Please define the search pattern first.");
                return;
            }
            DepItemComparer comparer = new DepItemComparer();
            List<DepItem> list = new List<DepItem>();
            DepItem depItem;
            string file;
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            try
            {
                foreach (var search in Config.SearchPatterns.Where(n => n.Enable))
                {
                    var results = Regex.Matches(p, search.SearchPattern);
                    foreach (var item in results)
                    {
                        file = item.ToString().Trim();
                        if (Config.IgnoredExtensions.Contains(new FileInfo(file).Extension.ToLower()))
                            continue;
                        depItem = new DepItem()
                        {
                            IsSelected = true,
                            //Value = Regex.Unescape(item.ToString())
                            Value = file
                        };
                        if (!list.Contains(depItem, comparer))
                        {
                            list.Add(depItem);
                            rtb.HighlightText.Add(file.Trim() + " ");
                            //Highlight(paragraphs, file);
                        }
                    }
                }
            }
            catch (Exception er)
            {
                MessageBox.Show(er.ToString(), "Error while executing search command.");
                return;
            }
            stopwatch.Stop();
            SearchResult = list;
            //RaisePropertyChanged(SearchResultPropertyName);
            ReplaceResult = new List<DepItemReplace>();
            MessageBox.Show(list.Count + " - " + stopwatch.ElapsedMilliseconds);
            WebBrowser webBrowser = (((((rtb.Parent as Grid).Parent as TabItem).Parent as TabControl)
                  .Items[1] as TabItem).Content as Grid).Children[0] as WebBrowser;
            StringBuilder sb = new StringBuilder();
/*            sb.Append(@"
<html>
    <head>
        <title>HTML Document</title>
        <style type='text/css'>
            body { width: 1000px; }
    	    .highlight { background-color: yellow;color: red; }
    	    p { display: block; font-family: tahoma; font-size: 14px; margin: 3px 0; width: 900px; }
    	</style>
    </head>
    <body>");*/
            string[] lines = p.Split('\n');

            bool isHighlight = false;
            int index = 0;
            int length = 0;
            foreach (var line in lines)
            {
                isHighlight = false;
                foreach (var item in list)
                {
                    if (line.Contains(item.Value))
                    {
                        index = line.IndexOf(item.Value);
                        length = item.Value.Length;
                        if (line.Length == index + length || line[index + length] == ' ')
                        {
                            isHighlight = true;
                            break;
                        }
                    }
                }
                if (isHighlight)
                {
                    sb.AppendFormat("<p>{0}<span class='highlight'>{1}</span>{2}</p>",
                                    line.Substring(0, index),
                                    line.Substring(index, length),
                                    (line.Length > index + length ? line.Substring(index + length + 1) : ""));
                    /*sb.Append("<p>" + line.Substring(0, index) + "<span class='highlight'>" +
                              line.Substring(index, length) + "</span>" +
                              (line.Length > index + length ? line.Substring(index + length) : "") + "</p>");*/
                }
                else
                {
                    sb.Append(@"<p>" + line + "</p>");
                }
            }
            //sb.Append(@"</body></html>");
            //webBrowser.NavigateToString(sb.ToString());
            try
            {
                //webBrowser.InvokeScript("setContent", sb.ToString());
                Html = sb.ToString();
            }
            catch (Exception ex)
            {
                string msg = "Could not call script: " + ex.Message;
                MessageBox.Show(msg);
            }
            TabIndex = 1;
        }

        private object _objLock = "lock";

        private bool FilterParagraph(Block paragraph)
        {
            string pText = new TextRange(paragraph.ContentStart, paragraph.ContentEnd).Text;
            Match match;
            string file;
            DepItem depItem;
            DepItemComparer comparer = new DepItemComparer();
            foreach (var search in Config.SearchPatterns.Where(n => n.Enable))
            {
                match = Regex.Match(pText, search.SearchPattern);
                if (match.Success)
                {
                    file = match.ToString().Trim();
                    if (Config.IgnoredExtensions.Contains(new FileInfo(file).Extension.ToLower()))
                        continue;
                    depItem = new DepItem()
                    {
                        IsSelected = true,
                        Value = file
                    };
                    lock (_objLock)
                    {
                        if (!SearchResult.Contains(depItem, comparer))
                        {
                            SearchResult.Add(depItem);
                        }
                    }
                    return true;
                }
            }
            return false;
        }

        private void Highlight(IEnumerable<Block> paragraphs, string search)
        {
            //var paragraphs = rtb.Document.Blocks.Where(n => n is Paragraph);
            TextRange textRange;
            var color = new SolidColorBrush(Colors.Blue);
            paragraphs.AsParallel().ForAll(paragraph =>
            {
                if (new TextRange(paragraph.ContentStart, paragraph.ContentEnd).Text.Contains(search + " "))
                {
                    paragraph.FontWeight = FontWeights.Bold;
                    paragraph.Foreground = color;
                }
            });
            //Thread.CurrentThread.
            /*foreach (var paragraph in paragraphs)
            {
                if (new TextRange(paragraph.ContentStart, paragraph.ContentEnd).Text.Contains(search + " "))
                {
                    paragraph.FontWeight = FontWeights.Bold;
                    paragraph.Foreground = new SolidColorBrush(Colors.Blue);
                }
            }*/
            //var runs = paragraphs.SelectMany(n => n.Resources);
            /*int index = 0;
            
            while ((index = content.IndexOf(search, index)) > 0)
            {
                start = rtb.Document.ContentStart.GetPositionAtOffset(index, LogicalDirection.Forward);
                end = rtb.Document.ContentStart.GetPositionAtOffset(index + search.Length, LogicalDirection.Backward);
                textRange = new TextRange(start, end);
                textRange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
                textRange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
                index = index + search.Length - 1;
            }*/
            /*TextRange range = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
            TextPointer start = range.Start.GetPositionAtOffset(match.Index, LogicalDirection.Forward);
            TextPointer end = range.Start.GetPositionAtOffset(match.Index + match.Length, LogicalDirection.Forward);
            var textrange = new TextRange(start, end);
            textrange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
            textrange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);*/
        }

        private void Highlight(RichTextBox rtb, string highligtText)
        {
            TextRange range = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
            //range.Text = @"TOP a multiline text or file END";
            Regex reg = new Regex("\\b" + Regex.Escape(highligtText) + "\\b\\s", RegexOptions.Compiled | RegexOptions.IgnoreCase);

            var start = rtb.Document.ContentStart;
            while (start != null && start.CompareTo(rtb.Document.ContentEnd) < 0)
            {
                if (start.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
                {
                    var match = reg.Match(start.GetTextInRun(LogicalDirection.Forward));
                    //var index = content.IndexOf(highligtText);
                    var textrange = new TextRange(start.GetPositionAtOffset(match.Index, LogicalDirection.Forward), start.GetPositionAtOffset(match.Index + match.Length + 3, LogicalDirection.Backward));
                    //var textrange = new TextRange(start.GetPositionAtOffset(index, LogicalDirection.Forward), start.GetPositionAtOffset(index + highligtText.Length, LogicalDirection.Backward));
                    textrange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
                    textrange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
                    start = textrange.End; // I'm not sure if this is correct or skips ahead too far, try it out!!!
                }
                start = start.GetNextContextPosition(LogicalDirection.Forward);
            }
        }

        private RelayCommand _replaceCommand;
        public RelayCommand ReplaceCommand
        {
            get
            {
                return _replaceCommand
                    ?? (_replaceCommand = new RelayCommand(
                                          () =>
                                          {
                                              if (Config.ReplacePatterns.Count <= 0)
                                              {
                                                  MessageBox.Show("Please define the replace pattern first.");
                                                  return;
                                              }

                                              if (SearchResult.Count <= 0)
                                                  return;
                                              DepItemReplaceComparer comparer = new DepItemReplaceComparer();
                                              List<DepItemReplace> list = new List<DepItemReplace>();
                                              DepItemReplace depItem;
                                              string replaceTmp;
                                              try
                                              {
                                                  foreach (var replace in Config.ReplacePatterns.Where(n => n.Enable))
                                                  {
                                                      foreach (var search in SearchResult.Where(n => n.IsSelected))
                                                      {
                                                          replaceTmp = replace.UseRegularExpression
                                                                           ? Regex.Replace(search.Value,
                                                                                           replace.SearchPattern,
                                                                                           replace.ReplacePattern)
                                                                           : search.Value.Replace(
                                                                               replace.SearchPattern,
                                                                               replace.ReplacePattern);
                                                          if (replaceTmp == search.Value)
                                                              continue;
                                                          depItem = new DepItemReplace()
                                                          {
                                                              IsSelected = true,
                                                              Value = search.Value,
                                                              ReplaceValue = replaceTmp
                                                          };
                                                          if (!list.Contains(depItem, comparer))
                                                              list.Add(depItem);
                                                      }
                                                  }
                                              }
                                              catch (Exception er)
                                              {
                                                  MessageBox.Show(er.ToString(), "Error while executing replace command.");
                                                  return;
                                              }
                                              
                                              ReplaceResult = list;
                                              MessageBox.Show(list.Count.ToString());
                                          },
                                          () => _searchResult.Count > 0));
            }
        }

        private RelayCommand _saveConfig;
        public RelayCommand SaveConfig
        {
            get
            {
                return _saveConfig
                    ?? (_saveConfig = new RelayCommand(
                                          () =>
                                          {
                                              try
                                              {
                                                  using (StreamWriter sw = new StreamWriter("Config.json", false))
                                                  {
                                                      sw.Write(Config.ToJsonString());
                                                  }
                                                  /*using (StreamWriter sw = new StreamWriter("Config.xml", false))
                                                  {
                                                      var tmp = JsonConvert.DeserializeXmlNode("{'root': " + Config.ToJsonString() + "}");
                                                      tmp.Save(sw);
                                                  }*/
                                              }
                                              catch (Exception er)
                                              {
                                                  MessageBox.Show(er.ToString(), "Error while persisting config file.");
                                              }
                                          }));
            }
        }

        private RelayCommand _loadConfig;
        public RelayCommand LoadConfig
        {
            get
            {
                return _loadConfig
                    ?? (_loadConfig = new RelayCommand(
                                          () =>
                                          {
                                              try
                                              {
                                                  FileInfo fi = new FileInfo("Config.json");
                                                  if (fi.Exists)
                                                  {
                                                      using (StreamReader sr = new StreamReader("Config.json"))
                                                      {
                                                          string json = sr.ReadToEnd();
                                                          if (string.IsNullOrWhiteSpace(json))
                                                              Config = new DepConfig();
                                                          else
                                                              Config = DepConfig.FromJson(json);
                                                      }
                                                  }
                                                  else
                                                      Config = new DepConfig();
                                              }
                                              catch (Exception er)
                                              {
                                                  MessageBox.Show(er.ToString(), "Error while reading config file.");
                                                  Config = new DepConfig();
                                              }
                                          }));
            }
        }

        private RelayCommand _copyCommand;
        public RelayCommand CopyCommand
        {
            get
            {
                return _copyCommand
                       ?? (_copyCommand = new RelayCommand(
                                              () =>
                                                  {
                                                      if (ReplaceResult.Count <= 0)
                                                          return;
                                                      CreateSeltExtractZip();
                                                      //CopyFiles();
                                                  },
                                              () => _replaceResult.Count > 0));
            }
        }

        private void CopyFiles()
        {
            FileInfo fi;
            foreach (var depItemReplace in _replaceResult.Where(n => n.IsSelected))
            {
                fi = new FileInfo(depItemReplace.ReplaceValue);
                if (!fi.Directory.Exists)
                    fi.Directory.Create();
                if (File.Exists(depItemReplace.Value))
                    File.Copy(depItemReplace.Value, depItemReplace.ReplaceValue, true);
            }
        }

        private void CreateSeltExtractZip()
        {
            using (ZipFile zip = new ZipFile())
            {
                zip.CompressionLevel = CompressionLevel.BestCompression;
                foreach (var depItemReplace in _replaceResult.Where(n => n.IsSelected))
                {
                    zip.AddFile(depItemReplace.Value, Path.GetDirectoryName(depItemReplace.ReplaceValue));
                }
                //zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath));
                //zip.Comment = "This will be embedded into a self-extracting console-based exe";
                SelfExtractorSaveOptions  options = new SelfExtractorSaveOptions ();
                options.Flavor = SelfExtractorFlavor.ConsoleApplication;
                options.ExtractExistingFile = ExtractExistingFileAction.OverwriteSilently;
                //options.DefaultExtractDirectory = "";
                //options.DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere";
                //options.PostExtractCommandLine = ExeToRunAfterExtract;
                //options.RemoveUnpackedFilesAfterExecute = true;
                zip.Save("Deploy\\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm") + ".zip");
                //zip.SaveSelfExtractor("Deploy\\" + DateTime.UtcNow.ToString("yyyy-MM-dd HH-mm") + ".exe", options);
            }
            //File.Move("archive", "Deploy\\archive");
        }

        ////public override void Cleanup()
        ////{
        ////    // Clean up if needed

        ////    base.Cleanup();
        ////}
    }
}